"""
"""

import os
import sys

import logging                                                                  # запись логов
import re                                                                       # регулярные выражения
import sqlite3
from datetime import date

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

sys.path.append('c:\PyLib')
from WebKit import *

const_dict = {'login': 'http://www.superjob.ru/',
              'pref':  'http://www.moyareklama.ru',
              'cookies': 'kaluga.txt'}
for key, value in const_dict.items():                                           # динамическое создание переменных
    globals()[key] = value



def main():
    global conn
    global window
    global stoday

    app = QApplication(sys.argv)

    today = date.today()
    stoday = today.strftime('%Y-%m-%d')                                         # дата в формате '2014-08-14'

    logging.basicConfig(filename='kaluga.log',level=logging.DEBUG,
        format = u'%(filename)s[LINE:%(lineno)d]# %(levelname)-8s %(message)s')

    conn = sqlite3.connect("kaluga.db")                                           # Имя в соответствии с именем модуля

    window = main_form()
    window.setWindowTitle("moyareklama.ru")
    window.resize(1000, 700)

    sys.exit(app.exec_())


class main_form(QWidget):

    def __init__(self, parent=None):
        super().__init__()

        self.ui()

    def ui(self):


        vbox_btn = QVBoxLayout()                                                    # вертикальный контейнер кнопок
        vbox_r = QVBoxLayout()                                                      # вертикальный контейнер справа

        btn_all = QPushButton("ПОЛНЫЙ ЦИКЛ")                                        # выполняет нажатие на все кнопки кроме ЛОГИН
        btn_all.clicked.connect(btn_all_click)
        vbox_btn.addWidget(btn_all)

        btn_collect_ref = QPushButton("Собрать ссылки")
        btn_collect_ref.clicked.connect(btn_collect_ref_click)
        vbox_btn.addWidget(btn_collect_ref)

        btn_del_old_ref = QPushButton("Удалить старые ссылки")
        btn_del_old_ref.clicked.connect(btn_del_old_ref_сlick)
        vbox_btn.addWidget(btn_del_old_ref)

        btn_add_ref = QPushButton("Сылки в архив")
        btn_add_ref.clicked.connect(btn_add_ref_click)
        vbox_btn.addWidget(btn_add_ref)

        btn_load_vacant = QPushButton("Собрать вакансии")
        vbox_btn.addWidget(btn_load_vacant)
        btn_load_vacant.clicked.connect(btn_load_vacant_click)

        btn_clean_org = QPushButton("Банить организации")
        btn_clean_org.clicked.connect(btn_clean_org_click)
        vbox_btn.addWidget(btn_clean_org)

        btn_merge_vacant = QPushButton("Слить в общую таблицу")
        btn_merge_vacant.clicked.connect(btn_merge_vacant_click)
        vbox_btn.addWidget(btn_merge_vacant)

        btn_quit = QPushButton("ВЫХОД")
        btn_quit.clicked.connect(btn_quit_click)
        vbox_btn.addWidget(btn_quit)

        btn_login = QPushButton("ЛОГИН")
        btn_login.clicked.connect(btn_login_click)
        vbox_btn.addWidget(btn_login)

        self.pb = QProgressBar()

        vbox_r.addWidget(self.pb)

        # горизонтальный Layout
        hbox = QHBoxLayout()
        hbox.addLayout(vbox_btn)
        hbox.addLayout(vbox_r)
        self.url = QLineEdit()

        self.web = Browser()
        vbox2 = QVBoxLayout()
        vbox2.addWidget(self.url)
        vbox2.addWidget(self.web)
        vbox2.addLayout(hbox)

        self.setLayout(vbox2)
        self.show()



def btn_all_click():
    btn_collect_ref_click()# собираем ссылки
    btn_del_old_ref_сlick()# удаляем старые ссылки
    btn_add_ref_click()# добавляем ссылки в архив
    btn_load_vacant_click()# загружаем вакансии
    btn_clean_org_click()# удаляем повторяющиеся и ненужные организации
    btn_merge_vacant_click()# сливаем вакансии в общую таблицу
    btn_quit_click()# выход


def no_space(line):
    return ''.join(c for c in line if ord(c) >= 32)


def cookies_load():
    if os.path.exists(cookies):                                                 # если cookies уже сохранены
        f = open(cookies, 'r')                                                  # Загузка куков из файла
        allC = tuple(cooky for li in f                                          # выражение итератор
            for cooky in QNetworkCookie.parseCookies(li.rstrip()))
        if allC:
            web.Сookies.setAllCookies(allC)
        f.close()


def btn_collect_ref_click():
    """
    Загружает куки.
    Собирает ссылки на вакансии с сайта и вставляет в таблицу vacant.
    """
    cookies_load()                                                              # грузим куки
    """ Создаем рабочую таблицу. Очищаем."""
    query = conn.cursor()                                                       
    query.execute("CREATE TABLE IF NOT EXISTS vacant "                          
        "(url TEXT DEFAULT '', org TEXT DEFAULT '',"
        "face TEXT DEFAULT '', phone TEXT DEFAULT '');")
    query.executescript("DELETE FROM vacant; VACUUM;")                          

    pref = 'http://www.moyareklama.ru'
    pattern_marker = re.compile('line_header')          # re <маркер> - есть ещё вакансии
    pattern_ref = re.compile('<a class="line_header" href="(.+?)" title=',re.S)# re ссылка на вакансию

    page = 97                                                                       
    while 1:

        """ Формируем url текущей страницы. Отображаем для контроля. Грузим 
        страницу."""
        url = ("http://www.moyareklama.ru/search"
            "/Калуга/718c5763d2175001077d38ae8d1a9055/{}/".format(page))
        window.url.setText(url)
        window.web.get(url)
        if pattern_marker.search(window.web.html):                                                                   
        
            """ Если ещё не пустая страница, собираем все ссылки на вакансии.
            Добавляем к ссылка название сайта . Формируем запрос."""
            list_ref = pattern_ref.findall(window.web.html)                        
            url = []                                                     
            for ref in list_ref:                                            
                url.append('("{}{}")'.format(pref, ref))                            
            sql = ('INSERT INTO vacant (url) VALUES {};'.                   
                   format(','.join(url)))                                
            try:                                                            
                query.execute(sql)                                          
            except sqlite3.Error as e:
                logging.error('btn_collect_ref_click: {} {}'.
                    format( e.args[0],sql))
                btn_quit_click()
            else:
                conn.commit()                                               
        else:                                                               
            break                                                           # больше страниц нет
        page += 1 # следующая страница
        
    query.close()

    # btn_collect_ref.setEnabled(False)                                           # гасим кнопку


def btn_del_old_ref_сlick():
    """
    Удаляем вчерашние ссылки.
    В запросе условие (AND '{}' > r.date;) необходимо, чтобы при повторном запуске
    выдавались сегодняшние вакансии, а не вакансии между запусками. Это защита от
    случайного повторного запуска программы.
    """
    query = conn.cursor()                                                       # курсор SQLite3

    query.execute("CREATE TABLE IF NOT EXISTS ref "                             # создаем архив ссылок
        "(date TEXT, url TEXT DEFAULT '');")

    sql = ("DELETE FROM vacant WHERE ROWID IN "                                 # удалить из ссылки сохраненные в ref
        "(SELECT v.ROWID FROM vacant as v, ref as r "                           # если дата архивной ссылки старая
        "WHERE (v.url= r.url) AND ('{}' > r.date));".format(stoday))

    try:
        query.execute(sql)                                                      # удаляем ссылки
    except sqlite3.Error as e:
        logging.error('btn_collect_ref_click: {} {}'.format( e.args[0],sql))
        btn_quit_click()
    else:
        conn.commit()

    query.close()

    # btn_del_old_ref.setEnabled(False)


def btn_add_ref_click():
    """
    Ссылки на вакансии из рабочей таблицы добавляются в архив ссылок.
    Записываем URL и текущую дату для контроля.
    """
    query = conn.cursor()

    query.execute("CREATE TABLE IF NOT EXISTS ref "                             # создаем архив ссылок
        "(date TEXT, url TEXT DEFAULT '');")

    sql = ("INSERT INTO ref (date, url) SELECT '{}', url FROM vacant;".         # сливаем ссылки в таблицу ref
        format(stoday))
    try:
        query.execute(sql)
    except sqlite3.Error as e:
        logging.error('btn_add_ref_click: {} {}'.format( e.args[0],sql))
        btn_quit_click()
    else:
        conn.commit()

    # btn_add_ref.setEnabled(False)


def btn_load_vacant_click():
    """
    Собирает описание вакансий.
    """
    cookies_load()                                                              # грузим куки
    pattern_face = re.compile('<span class="h_font_medium">(.+?)</span><br>')   # контактоное лицо
    pattern_org = re.compile('Место работы(.+?)line">(.+?)</div>',re.S)               # название компании
    pattern_org2 = re.compile('>(.+?)<')                                        # организация внутри html ссылки
    pattern_phone = re.compile('phones">(.+?)<div>(.+?)</div>',re.S)                 # контактный телефон

    select = conn.cursor()                                                      # для обхода записей
    update = conn.cursor()                                                      # для обновления записей

    select.execute("SELECT count(*) FROM vacant;")                              # количество записей
    rec = select.fetchone()                                                     # в таблице vacant
    window.pb.setRange(0,rec[0])                                                       # инициализация индикатора процесса

    select.execute("SELECT ROWID, url FROM vacant;")                            # запрос по всем записям таблицы
    ind = 0                                                                     # текущее значение прогресс бара

    for id, ref in select:                                                      # текущая запись и ссылка
        window.url.setText(ref)                                          #
        window.web.get(ref)                                                            # загружаем страницу вакансии
        
        ind += 1
        window.pb.setValue(ind)                                                        # индикатор процесса

        face = org = phone = ''                                             # очищаем переменные

        # m = pattern_face.search(web.html)                                       # ищем контактное лицо
        # if m:
            # face = m.group(1).strip()                                                   # если найдено - 1-я группа

        m = pattern_org.search(window.web.html)                                        # ищем название организации
        if m:
            org = m.group(2).strip()
            # org = org.strip(' \t\n\r')

        m = pattern_phone.search(window.web.html)                                      # ищем контактный телефон
        if m:
            phone = no_space(m.group(2).strip())                                        # если найдено - 1-я группа

        sql = ("UPDATE vacant SET face = '{}',org = ?,"                         #
            "phone = '{}' WHERE ROWID = {}".
            format(face, phone, id))
        try:
            update.execute(sql, (org,))                                         # обновляем запись
        except sqlite3.Error as e:
            logging.error('btn_load_vacant_click: {} {}'.format( e.args[0],sql))
            btn_quit_click()
        else:
            conn.commit()

    select.close()
    update.close()

    # btn_load_vacant.setEnabled(False)


def btn_clean_org_click():
    """
    Удаляет организации без телефонов.
    Удаляет дубли по телефонам и запрещенные организации.
    """
    query = conn.cursor()
    query.execute("DELETE FROM vacant WHERE (phone = '') OR (org = '');")       # удаляем пустые организации и телефоны

    query.execute("DELETE FROM vacant WHERE ROWID NOT IN ("                     # удалить дубли по имени организации
        "SELECT MIN(ROWID) FROM vacant GROUP BY org);")
    conn.commit()

    query.execute(                                                              # создаем таблицу организаций для бана
        "CREATE TABLE IF NOT EXISTS org (name TEXT DEFAULT '');")
    sql = ("DELETE FROM vacant WHERE ROWID IN "                                 # баним организации
        "(SELECT v.ROWID FROM vacant as v, org as r WHERE v.org=r.name);")
    query.execute(sql)
    conn.commit()

    # btn_clean_org.setEnabled(False)


def btn_merge_vacant_click():
    """
    Слияние вакансий в твблицу distrib
    """
    conn_out = sqlite3.connect("distrib.db")                                    # глобальная БД
    out = conn_out.cursor()
    out.execute("CREATE TABLE IF NOT EXISTS vacant "                            # создаем таблицу vacant
        "(url TEXT DEFAULT '',"                                                 # в глобальной базе
        " org TEXT DEFAULT '', org_find TEXT DEFAULT '',"
        " org_CRM TEXT DEFAULT '',face TEXT DEFAULT '',"
        " phone TEXT DEFAULT '', phone_find TEXT DEFAULT '',"
        " curator TEXT DEFAULT '');")

    query = conn.cursor()                                                       # локальная БД
    query.execute("SELECT url, org, face, phone FROM vacant;")                  # все записи из рабочей таблицы
    arr = query.fetchall()                                                      # загружаем в массив
    sql = "INSERT INTO vacant (url, org, face, phone) VALUES (?,?,?,?);"        # запрос с подстановкой
    try:
        out.executemany(sql, arr)                                               # повторять для каждой записи из массива
    except sqlite3.Error as e:
        logging.error('btn_add_ref_click: {} {}'.format( e.args[0],sql))
        btn_quit_click()
    else:
        conn_out.commit()                                                       # Завершаем транзакцию
    query.close()                                                               # закрыть курсоры
    out.close()                                                                 # закрыть курсоры
    conn_out.close()                                                            # закрыть соединение с удалённой базой

    # btn_merge_vacant.setEnabled(False)


def btn_quit_click():
    """
    Сохранение куков в файле и выход.
    """
    f = open(cookies, 'wb')
    allC = web.Сookies.allCookies()
    for c in allC:
        f.write(c.toRawForm()+'\r\n')
    f.close()
    conn.close()
    quit()


def btn_login_click():                                                          #
    """
    Cтраница регистрации пользователя.
    Пользователь должен залогонится. После этого необходимо сохранить куки.
    """
    cookies_load()                                                              # грузим куки
    url = const_dict['login']
    window.web.get('http://www.moyareklama.ru')

    # btn_login.setEnabled(False)


if __name__ == '__main__':

    main()
